home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Source.bin / InfoTipPanel.java < prev    next >
Text File  |  1998-09-23  |  63KB  |  2,068 lines

  1. package symantec.itools.awt;
  2.  
  3. import java.awt.*;
  4. import java.awt.event.ActionListener;
  5. import java.awt.event.ActionEvent;
  6. import java.awt.event.MouseAdapter;
  7. import java.awt.event.MouseEvent;
  8. import java.awt.image.ColorModel;
  9. import java.beans.PropertyVetoException;
  10. import java.beans.PropertyChangeEvent;
  11. import java.beans.PropertyChangeListener;
  12. import java.beans.VetoableChangeListener;
  13. import java.io.IOException;
  14. import java.net.URL;
  15. import symantec.itools.awt.ImagePanel;
  16. import symantec.itools.util.GeneralUtils;
  17. import symantec.itools.util.Timer;
  18. import java.util.ResourceBundle;
  19.  
  20. //  09/08/97    LAB    Conformed to Good Bean Spec.
  21. //    09/10/97    BJC Implementation of Serializable and bug fixes.
  22.  
  23. /**
  24.  * The InfoTipPanel is a panel which may contain only
  25.  * one component.
  26.  * Whenever this component receives a mouse entered event, the
  27.  * InfoTipPanel either scrolls a text message towards the button
  28.  * or pops the text up immediately next to the button.
  29.  * Whenever this component receives a mouse exit event, the
  30.  * text scrolls away from the component or disappears.
  31.  *
  32.  * @version 1.0, September 4, 1997
  33.  * @author Symantec
  34.  */
  35. public class InfoTipPanel extends ImagePanel implements Runnable, java.io.Serializable
  36. {
  37.     /**
  38.      * Defines the "from right" scroll direction.  One of four possible values for textLocation.
  39.      */
  40.     public static final int RIGHT_OF_COMPONENT = 0;
  41.     /**
  42.      * Defines the "from left" scroll direction.  One of four possible values for textLocation.
  43.      */
  44.     public static final int LEFT_OF_COMPONENT = 1;
  45.     /**
  46.      * Defines the "from top" scroll direction.  One of four possible values for textLocation.
  47.      */
  48.     public static final int ABOVE_COMPONENT = 2;
  49.     /**
  50.      * Defines the "from bottom" scroll direction.  One of four possible values for textLocation.
  51.      */
  52.     public static final int BELOW_COMPONENT = 3;
  53.  
  54.     /**
  55.      * Constructs a default InfoTipPanel.
  56.      * The panel is initialized with a null component, RIGHT_OF_COMPONENT as the scrolldirection,
  57.      * and the Auto-Position property set to true.
  58.      */
  59.     public InfoTipPanel()
  60.     {
  61.         this(null, "", RIGHT_OF_COMPONENT, true, 0, false);
  62.     }
  63.  
  64.     /**
  65.      * Constructs a new InfoTipPanel initialized with the
  66.      * specified component, text, minimum height and
  67.      * minimum width.
  68.      * @param component the component which will be added to the InfoTipPanel and
  69.      * whose mouse entered and mouse exited events will control the scrolling of the text.
  70.      * @param text the text which will be scrolled.
  71.      * @param textLocation the direction from which the text is scrolled.
  72.      * resize itself and position the component placed inside of it.
  73.      */
  74.     public InfoTipPanel(Component component, String text, int textLocation, boolean textScrollsIn, int textDelay, boolean highlightText)
  75.     {
  76.         scrollState = DONE_SCROLLING_OUT;  //the text starts fully extended, waiting to start scrolling in on a mouse entered event
  77.  
  78.         myMouseListener = new Mouse();
  79.         delayTimer = new Timer();
  80.         if (delayTimer != null)
  81.             delayTimer.addActionListener(new Action());
  82.  
  83.  
  84.         super.setLayout(null);
  85.  
  86.         try
  87.         {
  88.             setHighlightText(highlightText);
  89.             setTextHighlightColor(new Color(16776960));    //default to yellow
  90.             setTextColor(new Color(0));    //default to black
  91.             setTextScrollsIn(textScrollsIn);
  92.             setTextDelay(textDelay);
  93.             setText(text);
  94.             setTextLocation(textLocation);
  95.         }
  96.         catch(PropertyVetoException e) {}
  97.  
  98.         setComponent(component);
  99.     }
  100.  
  101.     /**
  102.      * Set the amount of time (in milliseconds) to wait after a mouse
  103.      * entered event before the text is scrolled in or popped up.
  104.      *
  105.      * @param newDelay the amount of time (in milliseconds) to wait after a mouse
  106.      * entered event before the text is scrolled in or popped up.
  107.      * @see #getTextDelay
  108.      * @exception PropertyVetoException
  109.      * if the specified property value is unacceptable
  110.      */
  111.     public void setTextDelay(int newDelay) throws PropertyVetoException
  112.     {
  113.         if (textDelay != newDelay)
  114.         {
  115.             Integer oldTextDelay = new Integer(textDelay);
  116.             Integer newTextDelay = new Integer(newDelay);
  117.  
  118.             vetos.fireVetoableChange("textDelay", oldTextDelay, newTextDelay);
  119.  
  120.             textDelay = newDelay;
  121.             delayTimer.setDelay(textDelay); //let the timer know that we will want a new delay time
  122.  
  123.             changes.firePropertyChange("textDelay", oldTextDelay, newTextDelay);
  124.         }
  125.     }
  126.  
  127.     /**
  128.      * Get the amount of time (in milliseconds) to wait after a mouse
  129.      * entered event before the text is scrolled in or popped up.
  130.      *
  131.      * @return the amount of time (in milliseconds) that this InfoTipPanel
  132.      * will wait after a mouse entered event before the text is scrolled in
  133.      * or popped up.
  134.      * @see #setTextDelay
  135.      */
  136.     public int getTextDelay()
  137.     {
  138.         return textDelay;
  139.     }
  140.  
  141.  
  142.     /**
  143.      * Sets the "Text Scrolls In" property which tells the InfoTipPanel whether
  144.      * the text should be scrolled in on a mouse over rather than just appearing
  145.      * next to the component (without scrolling in).
  146.      *
  147.      * @param newScroll the new value for this property
  148.      * @see #getTextScrollsIn
  149.      * @exception PropertyVetoException
  150.      * if the specified property value is unacceptable
  151.      */
  152.     public void setTextScrollsIn(boolean newScroll) throws PropertyVetoException
  153.     {
  154.         if (textScrollsIn != newScroll)
  155.         {
  156.             Boolean oldTextScrollsIn = new Boolean(textScrollsIn);
  157.             Boolean newTextScrollsIn = new Boolean(newScroll);
  158.  
  159.             vetos.fireVetoableChange("textScrollsIn", oldTextScrollsIn, newTextScrollsIn);
  160.  
  161.             stopScrolling();
  162.             textScrollsIn = newScroll;
  163.  
  164.             changes.firePropertyChange("textScrollsIn", oldTextScrollsIn, newTextScrollsIn);
  165.         }
  166.     }
  167.  
  168.  
  169.     /**
  170.      * Gets the value of the "Text Scrolls In" property which tells the
  171.      * InfoTipPanel whether the text should be scrolled in on a mouse over
  172.      * rather than just appearing next to the component (without scrolling in).
  173.      *
  174.      * @return the current value of this property
  175.      * @see #setTextScrollsIn
  176.      */
  177.     public boolean getTextScrollsIn()
  178.     {
  179.         return textScrollsIn;
  180.     }
  181.  
  182.  
  183.     /**
  184.      * Sets where the text will be located in this InfoTipPanel. This controls
  185.      * where the text will appear on a mouse entered event.
  186.      * Causes the component to redraw.
  187.      *
  188.      * @param newLocation the new text location: RIGHT_OF_COMPONENT,
  189.      * LEFT_OF_COMPONENT, ABOVE_COMPONENT, or BELOW_COMPONENT
  190.      * @see #getTextLocation
  191.      * @exception PropertyVetoException
  192.      * if the specified property value is unacceptable
  193.      */
  194.     public void setTextLocation(int newLocation) throws PropertyVetoException
  195.     {
  196.         if (textLocation != newLocation)
  197.         {
  198.             Integer oldTextLocation = new Integer(textLocation);
  199.             Integer newTextLocation = new Integer(newLocation);
  200.  
  201.             vetos.fireVetoableChange("textLocation", oldTextLocation, newTextLocation);
  202.  
  203.             stopScrolling();
  204.             textLocation = newLocation;
  205.  
  206.             createOffScreenBuffers();
  207.  
  208.             repaint();
  209.  
  210.             changes.firePropertyChange("textLocation", oldTextLocation, newTextLocation);
  211.         }
  212.     }
  213.  
  214.  
  215.     /**
  216.      * Gets the current value of the "Text Location" property for this InfoTipPanel.
  217.      * The text location controls where the text will appear on a mouse entered event.
  218.      *
  219.      * @return the text location: RIGHT_OF_COMPONENT, LEFT_OF_COMPONENT,
  220.      * ABOVE_COMPONENT, or BELOW_COMPONENT
  221.      * @see #setTextLocation
  222.      */
  223.     public int getTextLocation()
  224.     {
  225.         return textLocation;
  226.     }
  227.  
  228.     /**
  229.      * Sets the text in this InfoTipPanel. This is the text
  230.      * which scrolls out on a mouse entered event.
  231.      * Causes the component to redraw.
  232.      *
  233.      * @param newText the text
  234.      * @exception PropertyVetoException
  235.      * if the specified property value is unacceptable
  236.      * @see #getText
  237.      */
  238.     public void setText(String newText) throws PropertyVetoException
  239.     {
  240.         if (!symantec.itools.util.GeneralUtils.objectsEqual(text, newText))
  241.         {
  242.             String oldText = text;
  243.  
  244.             vetos.fireVetoableChange("text", oldText, newText);
  245.  
  246.             stopScrolling();
  247.  
  248.             text = newText;
  249.  
  250.             calculateTextAttributes();
  251.  
  252.             changes.firePropertyChange("text", oldText, newText);
  253.         }
  254.     }
  255.  
  256.  
  257.     /**
  258.      * Gets the current text in this InfoTipPanel. This is the text
  259.      * which scrolls out on a mouse entered event.
  260.      *
  261.      * @return the text
  262.      * @see #setText
  263.      */
  264.     public String getText()
  265.     {
  266.         return new String(text);
  267.     }
  268.  
  269.  
  270.     /**
  271.      * Sets the text color.
  272.      * Causes the InfoTipPanel to redraw.
  273.      * @param color the new color for the text
  274.      * @see #getTextColor
  275.      * @exception PropertyVetoException
  276.      * if the specified property value is unacceptable
  277.      */
  278.     public void setTextColor(Color color) throws PropertyVetoException
  279.     {
  280.         if(! symantec.itools.util.GeneralUtils.objectsEqual(textColor, color))
  281.         {
  282.             Color oldValue = textColor;
  283.  
  284.             vetos.fireVetoableChange("textColor", oldValue, color);
  285.  
  286.             if (textAnimationThread != null)
  287.                 textAnimationThread.suspend();
  288.  
  289.             textColor = color;
  290.  
  291.             repaint();
  292.  
  293.             if (textAnimationThread != null)
  294.                 textAnimationThread.resume();
  295.  
  296.             changes.firePropertyChange("textColor", oldValue, color);
  297.         }
  298.     }
  299.  
  300.     /**
  301.      * Gets the current text color.
  302.      * @return the current color of the text
  303.      * @see #setTextColor
  304.      */
  305.     public Color getTextColor()
  306.     {
  307.         return textColor;
  308.     }
  309.  
  310.  
  311.     /**
  312.      * Sets the current highlight color which will be used if highlightText
  313.      * is true.
  314.      *
  315.      * @param color the new highlight color
  316.      * @exception PropertyVetoException
  317.      * if the specified property value is unacceptable
  318.      * @see #getTextHighlightColor
  319.      * @see #getHighlightText
  320.      * @see #setHighlightText
  321.      */
  322.     public void setTextHighlightColor(Color color) throws PropertyVetoException
  323.     {
  324.         if (!GeneralUtils.objectsEqual(textHighlightColor, color))
  325.         {
  326.             Color oldValue = textHighlightColor;
  327.  
  328.             vetos.fireVetoableChange("textHighlightColor", oldValue, color);
  329.  
  330.             if (textAnimationThread != null)
  331.                 textAnimationThread.suspend();
  332.  
  333.             textHighlightColor = color;
  334.  
  335.             if (highlightText)
  336.                 repaint();
  337.  
  338.             if (textAnimationThread != null)
  339.                 textAnimationThread.resume();
  340.  
  341.             changes.firePropertyChange("textHighlightColor", oldValue, color);
  342.         }
  343.     }
  344.  
  345.  
  346.     /**
  347.      * Gets the current highlight color which will be used if highlightText
  348.      * is true.
  349.      *
  350.      * @return the current highlight color
  351.      * @see #setTextHighlightColor
  352.      * @see #getHighlightText
  353.      * @see #setHighlightText
  354.      */
  355.     public Color getTextHighlightColor()
  356.     {
  357.         return textHighlightColor;
  358.     }
  359.  
  360.  
  361.     /**
  362.      * Sets the "Highlight Text" property which determines whether the text
  363.      * will be highlighted with the current textHighlightColor or not.
  364.      *
  365.      * @param newValue the new value of this property
  366.      * @exception PropertyVetoException
  367.      * if the specified property value is unacceptable
  368.      * @see #getHighlightText
  369.      * @see #getTextHighlightColor
  370.      * @see #setTextHighlightColor
  371.      */
  372.     public void setHighlightText(boolean newValue) throws PropertyVetoException
  373.     {
  374.         if (highlightText != newValue)
  375.         {
  376.             stopScrolling();
  377.  
  378.             Boolean oldHighlightText = new Boolean(highlightText);
  379.             Boolean newHighlightText = new Boolean(newValue);
  380.  
  381.             vetos.fireVetoableChange("highlightText", oldHighlightText, newHighlightText);
  382.  
  383.             highlightText = newValue;
  384.  
  385.             extraHighlightWidth = (highlightText ? 6 : 0);
  386.             extraHighlightHeight = (highlightText ? 4 : 0);
  387.  
  388.             changes.firePropertyChange("highlightText", oldHighlightText, newHighlightText);
  389.         }
  390.     }
  391.  
  392.  
  393.     /**
  394.      * Gets the current value of the "Highlight Text" property which
  395.      * determines whether the text will be highlighted with the current
  396.      * textHighlightColor or not.
  397.      *
  398.      * @return the current value of this property
  399.      * @see #setHighlightText
  400.      * @see #getTextHighlightColor
  401.      * @see #setTextHighlightColor
  402.      */
  403.     public boolean getHighlightText()
  404.     {
  405.         return highlightText;
  406.     }
  407.  
  408.     /**
  409.      * Reshapes the Component to the specified bounding box.
  410.      *
  411.      * It has been OVERRIDDEN here to adjust all the animation buffers
  412.      * to the correct size (based on the new size of the InfoTipPanel)
  413.      *
  414.      * @param x the x coordinate
  415.      * @param y the y coordinate
  416.      * @param width the width of the component
  417.      * @param height the height of the component
  418.      * @see java.awt.Component#getBounds
  419.      * @see java.awt.Component#setLocation
  420.      * @see java.awt.Component#setSize
  421.      */
  422.     public void setBounds(int x, int y, int width, int height)
  423.     {
  424.         stopScrolling();
  425.  
  426.         super.setBounds(x, y, width, height);
  427.  
  428.         createOffScreenBuffers();
  429.     }
  430.  
  431.     /**
  432.      * Sets the URL of the image to display in this panel.
  433.      *
  434.      * It has been OVERRIDDEN here to suspend the animation thread while the image
  435.      * is set, and to re-buffer the new background.
  436.      *
  437.      * @param url the URL of the image to display
  438.      * @see symantec.itools.awt.ImagePanel#setImageURL
  439.      * @see symantec.itools.awt.ImagePanel#getImageURL
  440.      * @exception PropertyVetoException
  441.      * if the specified property value is unacceptable
  442.      */
  443.     public void setImageURL(URL url) throws PropertyVetoException
  444.     {
  445.         if (textAnimationThread != null)
  446.             textAnimationThread.suspend();
  447.  
  448.         super.setImageURL(url);
  449.         storeBackground();
  450.  
  451.         if (textAnimationThread != null)
  452.             textAnimationThread.resume();
  453.     }
  454.  
  455.     /**
  456.      * Sets the new panel image style.
  457.      *
  458.      * It has been OVERRIDDEN here to suspend the animation thread while the style
  459.      * is set, and to re-buffer the new background.
  460.      *
  461.      * @param newStyle the new panel image style, one of
  462.      * IMAGE_TILED, IMAGE_CENTERED, or IMAGE_SCALED_TO_FIT
  463.      * @exception PropertyVetoException
  464.      * if the specified property value is unacceptable
  465.      * @see symantec.itools.awt.ImagePanel#setStyle
  466.      * @see symantec.itools.awt.ImagePanel#getStyle
  467.      * @see symantec.itools.awt.ImagePanel#IMAGE_TILED
  468.      * @see symantec.itools.awt.ImagePanel#IMAGE_CENTERED
  469.      * @see symantec.itools.awt.ImagePanel#IMAGE_SCALED_TO_FIT
  470.      */
  471.     public void setStyle(int newStyle) throws PropertyVetoException
  472.     {
  473.         if (textAnimationThread != null)
  474.             textAnimationThread.suspend();
  475.  
  476.         super.setStyle(newStyle);
  477.         storeBackground();
  478.  
  479.         if (textAnimationThread != null)
  480.             textAnimationThread.resume();
  481.     }
  482.  
  483.     /**
  484.      * Sets the component in this InfoTipPanel. This is the component
  485.      * that triggers text scrolling on a mouse entered event. The InfoTipPanel
  486.      * can only contain one component.  Any previous component will be
  487.      * removed before the new one is added.
  488.      *
  489.      * @param comp the component to add
  490.      * @see #getComponent
  491.      */
  492.     public void setComponent(Component comp)
  493.     {
  494.         removeAll();    //we rely on the fact that removeAll() calls stopScrolling()
  495.  
  496.         myComponent = comp;
  497.  
  498.         //Add the new component at the end
  499.         callingAddInternally = true;
  500.         try
  501.         {
  502.             if (comp != null)
  503.                 super.add(myComponent);
  504.         }
  505.         finally
  506.         {
  507.             callingAddInternally = false;
  508.         }
  509.  
  510.         createOffScreenBuffers();
  511.  
  512.         if (myComponent != null)
  513.             myComponent.addMouseListener(myMouseListener);
  514.     }
  515.  
  516.     /**
  517.      * Gets the current component in the InfoTipPanel.
  518.      * This is the component that triggers text scrolling on a
  519.      * mouse entered event.
  520.      * @return the current component in the InfoTipPanel
  521.      * @see #setComponent
  522.      */
  523.     public Component getComponent()
  524.     {
  525.         return myComponent;
  526.     }
  527.  
  528.     /**
  529.      * Sets the font of the component.
  530.      * This is a standard method of java.awt.component.
  531.      *
  532.      * It has been OVERRIDDEN here to recalculate the sizes/locations
  533.      * of everything based on the new font.
  534.      *
  535.      * @param f the font
  536.      */
  537.     public synchronized void setFont(Font f)
  538.     {
  539.         stopScrolling();
  540.         super.setFont(f);
  541.  
  542.         calculateTextAttributes();
  543.         createOffScreenBuffers();
  544.     }
  545.  
  546.     /**
  547.      * Returns the recommended dimensions to properly display this component.
  548.      * This is a standard Java AWT method which gets called to determine
  549.      * the recommended size of this component.
  550.      *
  551.      * It has been OVERRIDEEN here to, for each axis, return the larger of
  552.      * the current size and the size of myComponent.
  553.      *
  554.      * @return A Dimension representing the preferred size of this component
  555.      * @see #getMinimumSize
  556.      */
  557.     public Dimension getPreferredSize()
  558.     {
  559.         int preferredWidth = Math.max(getSize().width, myComponent == null ? 0 : myComponent.getSize().width);
  560.         int preferredHeight = Math.max(getSize().height, myComponent == null ? 0 : myComponent.getSize().height);
  561.  
  562.         return new Dimension(preferredWidth, preferredHeight);
  563.     }
  564.  
  565.     /**
  566.      * Returns the minimum dimensions to properly display this component.
  567.      * This is a standard Java AWT method which gets called to determine
  568.      * the minimum size of this component.
  569.      *
  570.      * It is OVERRIDDEN here to just return the preferred size as determined
  571.      * by a call to getPreferredSize()
  572.      *
  573.      * @return A Dimension representing the minimum size of this component
  574.      * @see #getPreferredSize
  575.      */
  576.     public Dimension getMinimumSize()
  577.     {
  578.             return getPreferredSize();
  579.     }
  580.  
  581.     /**
  582.      * Takes no action.
  583.      * This is a standard Java AWT method which gets called to specify
  584.      * which layout manager should be used to layout the components in
  585.      * standard containers.
  586.      *
  587.      * Since layout managers CANNOT BE USED with this container the standard
  588.      * setLayout has been OVERRIDDEN for this container and does nothing.
  589.      *
  590.      * @param l the layout manager to use to layout this container's components
  591.      * (IGNORED)
  592.      * @see java.awt.Container#getLayout
  593.      **/
  594.     public void setLayout(LayoutManager mgr)
  595.     {
  596.     }
  597.  
  598.     /**
  599.      * Paints this component using the given graphics context.
  600.      * This is a standard Java AWT method which typically gets called
  601.      * by the AWT to handle painting this component. It paints this component
  602.      * using the given graphics context. The graphics context clipping region
  603.      * is set to the bounding rectangle of this component and its [0,0]
  604.      * coordinate is this component's top-left corner.
  605.      *
  606.      * It has been OVERRIDDEN for this container to call super.paint(g), and
  607.      * then draw it's text, if necessary.
  608.      *
  609.      * @param g the graphics context used for painting
  610.      * @see java.awt.Component#repaint
  611.      */
  612.     public void paint(Graphics g)
  613.     {
  614.         super.paint(g);
  615.  
  616.         if (!dontDrawText)
  617.         {
  618.             if (scrollState == DONE_SCROLLING_IN)
  619.             {
  620.                 //If the text is supposed to be scrolled in all the way, we must draw it.
  621.                 //Note that if the text is in the process of scrolling in or out the animation
  622.                 //thread will handle drawing it at the appropriate time.
  623.                 Point textPosition = getTextFinishingPoint();
  624.                 drawText(g, textPosition.x, textPosition.y);
  625.             }
  626.         }
  627.     }
  628.  
  629.     /**
  630.      * Tells this component that it has been added to a container.
  631.      * This is a standard Java AWT method which gets called by the AWT when
  632.      * this component is added to a container. Typically, it is used to
  633.      * create this component's peer.
  634.      *
  635.      * It has been OVERRIDDEN here to calculate text sizes and to create
  636.      * any off screen buffers that will be needed for the animation.
  637.      *
  638.      * @see #removeNotify
  639.      */
  640.     public synchronized void addNotify()
  641.     {
  642.         super.addNotify();
  643.         errors = ResourceBundle.getBundle("symantec.itools.resources.ErrorsBundle");
  644.  
  645.         //re-calculate sizes and re-create off screen images now that they have a graphics context
  646.         calculateTextAttributes();
  647.         createOffScreenBuffers();
  648.  
  649.         locationVeto = new LocationVeto();
  650.         addTextLocationListener(locationVeto);
  651.     }
  652.  
  653.     /**
  654.      * Tells this component that it is being removed from a container.
  655.      * This is a standard Java AWT method which gets called by the AWT when
  656.      * this component is removed from a container. Typically, it is used to
  657.      * destroy the peers of this component and all its subcomponents.
  658.      *
  659.      * It has been OVERRIDDEN here to dispose of any buffers or any
  660.      * animation thread in use by this InfoTipPanel, and then
  661.      * call super.removeNotify().
  662.      *
  663.      * @see #addNotify
  664.      */
  665.     public synchronized void removeNotify()
  666.     {
  667.         stopScrolling();
  668.  
  669.         if (offScreenImage != null)
  670.         {
  671.             offScreenImage.flush();
  672.             offScreenImage = null;
  673.         }
  674.  
  675.         if (bufferedBgndImage != null)
  676.         {
  677.             bufferedBgndImage.flush();
  678.             bufferedBgndImage = null;
  679.         }
  680.  
  681.         removeTextLocationListener(locationVeto);
  682.         locationVeto = null;
  683.  
  684.         super.removeNotify();
  685.     }
  686.  
  687.     /**
  688.      * Standard method in the java.lang.Runnable interface.
  689.      * When an object implementing interface <code>Runnable</code> is used
  690.      * to create a thread, starting the thread causes the object's
  691.      * <code>run</code> method to be called in that separately executing
  692.      * thread.
  693.      *
  694.      * Here, run has been implemented to do animate the text scrolling in and out.
  695.      * The thread calling this method will continue to animate the text until it
  696.      * scrolls either completely out, or completely in.
  697.      *
  698.      * @see     java.lang.Thread#run
  699.      * @since   JDK1.0
  700.      */
  701.     public void run()
  702.     {
  703.         if (text != null && validateOffScreenBuffers())
  704.         {
  705.             switch (scrollState) {
  706.                 case SCROLLING_OUT:
  707.                     if (textScrollsIn)
  708.                         scrollText();
  709.                     else
  710.                         popDownText();
  711.                     break;
  712.                 case SCROLLING_IN:
  713.                     if (textScrollsIn)
  714.                         scrollText();
  715.                     else
  716.                         popUpText();
  717.                     break;
  718.             }
  719.         }
  720.  
  721.         textAnimationThread = null;
  722.     }
  723.  
  724.     /**
  725.      * Removes the specified component from this container.
  726.      * This is a standard Java AWT method which gets called to remove a
  727.      * component from a container. When this happens the component's
  728.      * removeNotify() will also get called to indicate component removal.
  729.      *
  730.      * It has been OVERRIDDEN here to add remove the listeners which we added
  731.      * to the component.
  732.      *
  733.      * @param comp the component to remove
  734.      * @see #removeAll
  735.      * @see java.awt.Container#add
  736.      */
  737.     public synchronized void remove(Component comp)
  738.     {
  739.         stopScrolling();
  740.  
  741.         super.remove(comp);
  742.  
  743.         if (comp == myComponent)
  744.         {
  745.             myComponent.removeMouseListener(myMouseListener);
  746.             myComponent = null;
  747.         }
  748.     }
  749.  
  750.     /**
  751.      * Removes all the components from this container.
  752.      * This is a standard Java AWT method which gets called to remove all
  753.      * the components from a container. When this happens each component's
  754.      * removeNotify() will also get called to indicate component removal.
  755.      *
  756.      * It has been OVERRIDDEN here to remove the InfoTipPanel's
  757.      * reference to its component.
  758.      *
  759.      * @see #remove
  760.      * @see java.awt.Container#add
  761.      */
  762.     public synchronized void removeAll()
  763.     {
  764.         stopScrolling();
  765.  
  766.         if (this.myComponent != null)
  767.             super.remove(this.myComponent);
  768.  
  769.         myComponent = null;
  770.     }
  771.  
  772.     /**
  773.      * Adds a listener for all event changes.
  774.      * @param listener the listener to add.
  775.      * @see #removePropertyChangeListener
  776.      */
  777.     public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
  778.     {
  779.         super.addPropertyChangeListener(listener);
  780.         changes.addPropertyChangeListener(listener);
  781.     }
  782.  
  783.     /**
  784.      * Removes a listener for all event changes.
  785.      * @param listener the listener to remove.
  786.      * @see #addPropertyChangeListener
  787.      */
  788.     public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
  789.     {
  790.         super.removePropertyChangeListener(listener);
  791.         changes.removePropertyChangeListener(listener);
  792.     }
  793.  
  794.     /**
  795.      * Adds a vetoable listener for all event changes.
  796.      * @param listener the listener to add.
  797.      * @see #removeVetoableChangeListener
  798.      */
  799.     public synchronized void addVetoableChangeListener(VetoableChangeListener listener)
  800.     {
  801.         super.addVetoableChangeListener(listener);
  802.         vetos.addVetoableChangeListener(listener);
  803.     }
  804.  
  805.     /**
  806.      * Removes a vetoable listener for all event changes.
  807.      * @param listener the listener to remove.
  808.      * @see #addVetoableChangeListener
  809.      */
  810.     public synchronized void removeVetoableChangeListener(VetoableChangeListener listener)
  811.     {
  812.         super.removeVetoableChangeListener(listener);
  813.         vetos.removeVetoableChangeListener(listener);
  814.     }
  815.  
  816.     /**
  817.      * Adds a listener for the TextLocation property changes.
  818.      * @param listener the listener to add.
  819.      * @see #removeTextLocationListener(java.beans.PropertyChangeListener)
  820.      */
  821.     public synchronized void addTextLocationListener(PropertyChangeListener listener)
  822.     {
  823.         changes.addPropertyChangeListener("textLocation", listener);
  824.     }
  825.  
  826.     /**
  827.      * Removes a listener for the TextLocation property changes.
  828.      * @param listener the listener to remove.
  829.      * @see #addTextLocationListener(java.beans.PropertyChangeListener)
  830.      */
  831.     public synchronized void removeTextLocationListener(PropertyChangeListener listener)
  832.     {
  833.         changes.removePropertyChangeListener("textLocation", listener);
  834.     }
  835.  
  836.     /**
  837.      * Adds a vetoable listener for the TextLocation property changes.
  838.      * @param listener the listener to add.
  839.      * @see #removeTextLocationListener(java.beans.VetoableChangeListener)
  840.      */
  841.     public synchronized void addTextLocationListener(VetoableChangeListener listener)
  842.     {
  843.         vetos.addVetoableChangeListener("textLocation", listener);
  844.     }
  845.  
  846.     /**
  847.      * Removes a vetoable listener for the TextLocation property changes.
  848.      * @param listener the listener to remove.
  849.      * @see #addTextLocationListener(java.beans.VetoableChangeListener)
  850.      */
  851.     public synchronized void removeTextLocationListener(VetoableChangeListener listener)
  852.     {
  853.         vetos.removeVetoableChangeListener("textLocation", listener);
  854.     }
  855.  
  856.     /**
  857.      * Implementation of the MouseListener interface so that we can trigger
  858.      * text scrolling on mouseEntered() and mouseExited().
  859.      */
  860.     class Mouse extends MouseAdapter
  861.     {
  862.         public void mouseEntered(MouseEvent e)
  863.         {
  864.             startScrolling(SCROLLING_IN, false);
  865.         }
  866.  
  867.         public void mouseExited(MouseEvent e)
  868.         {
  869.             startScrolling(SCROLLING_OUT, false);
  870.         }
  871.     }
  872.  
  873.     /**
  874.      * Implementation of the ActionListener interface so that we can trigger
  875.      * text scrolling after a delay.
  876.      */
  877.     class Action implements ActionListener
  878.     {
  879.         public void actionPerformed(ActionEvent e)
  880.         {
  881.             startScrolling(SCROLLING_IN, true);  //true so that we don't start another timer and wait again
  882.         }
  883.     }
  884.  
  885.  
  886.     /**
  887.      * This is the PropertyChangeEvent handling inner class for the constrained TextLocation property.
  888.      * Handles vetoing TextLocations that are outside of the valid range.
  889.      */
  890.     class LocationVeto implements java.beans.VetoableChangeListener, java.io.Serializable
  891.     {
  892.         /**
  893.          * This method gets called when an attempt to change the constrained TextLocation property is made.
  894.          * Ensures the given text location is valid.
  895.          *
  896.          * @param     e a <code>PropertyChangeEvent</code> object describing the
  897.          *             event source and the property that has changed.
  898.          * @exception PropertyVetoException if the recipient wishes the property
  899.          *              change to be rolled back.
  900.          */
  901.         public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException
  902.         {
  903.             int i = ((Integer)e.getNewValue()).intValue();
  904.             if (!isValidTextLocation(i))
  905.             {
  906.                 throw new PropertyVetoException(errors.getString("InvalidTextLocation") + i, e);
  907.             }
  908.         }
  909.     }
  910.  
  911.     /**
  912.      * Is the given text location valid.
  913.      * @param i the given text location
  914.      * @return true if the given text location is acceptable, false if not.
  915.      */
  916.     protected boolean isValidTextLocation(int i)
  917.     {
  918.         switch(i)
  919.         {
  920.             case RIGHT_OF_COMPONENT:
  921.             case LEFT_OF_COMPONENT:
  922.             case ABOVE_COMPONENT:
  923.             case BELOW_COMPONENT:
  924.                 return true;
  925.             default:
  926.                 return false;
  927.         }
  928.     }
  929.  
  930.  
  931.     /**
  932.      * Draws the current text into the graphics context and location specified
  933.      * in the parameters.  Highlights the text, if necessary.
  934.      *
  935.      * @param g the graphics context into which the text should be drawn
  936.      * @param x the x-coordinate at which the text should be drawn
  937.      * @param y the y-coordinate at which the text should be drawn
  938.      */
  939.     protected void drawText(Graphics g, int x, int y)
  940.     {
  941.         Color savedColor = g.getColor();
  942.  
  943.         if (highlightText)
  944.         {
  945.             g.setColor(textHighlightColor);
  946.  
  947.             Rectangle highlightBounds = getHighlightBounds(x, y);
  948.             if (highlightBounds != null)
  949.                 g.fillRect(highlightBounds.x, highlightBounds.y, highlightBounds.width, highlightBounds.height);
  950.  
  951.             g.setColor(textColor);
  952.  
  953.             g.drawRect(highlightBounds.x,
  954.                         highlightBounds.y,
  955.                         highlightBounds.width - 1,
  956.                         highlightBounds.height - 1);
  957.                     // -1 so that we fit the rectangle within the bounds
  958.         }
  959.  
  960.         g.setColor(textColor);
  961.         g.drawString(text, x, y);
  962.         g.setColor(savedColor);
  963.     }
  964.  
  965.     /**
  966.      * Determines the rectangle which bounds the area that should be highlighted.
  967.      *
  968.      * @return this bounding Rectangle, or null if textWidth or textHeight is zero.
  969.      */
  970.     protected Rectangle getHighlightBounds(int textX, int textY)
  971.     {
  972.         if (textWidth > 0 && textHeight > 0)
  973.             return new Rectangle(textX - extraHighlightWidth/2,
  974.                                     textY - textAscent - extraHighlightHeight/2,
  975.                                     textWidth + extraHighlightWidth,
  976.                                     textHeight + extraHighlightHeight);
  977.         else
  978.             return null;
  979.     }
  980.  
  981.     /**
  982.      * Starts the text scrolling in the direction indicated by the parameter
  983.      * newScrollState.  The text will continue to be scrolled back and
  984.      * forth until the text scrolls all the way in, or all the way out.
  985.      * Causes a thread to be started to animate the text.
  986.      *
  987.      * This method is also responsible for starting the timer if the textDelay
  988.      * is set to some number of milliseconds.  This method will be called a
  989.      * second time if and when that timer goes off.
  990.      *
  991.      * @param newScrollState the direction of the scroll: SCROLLING_IN,
  992.      * or SCROLLING_OUT
  993.      * @param ignoreDelay whether or not to ignore a delay even if it would normally
  994.      * be necessary (i.e. even though the text is starting to scroll in)
  995.      * @see #stopScrolling
  996.      */
  997.     protected void startScrolling(int newScrollState, boolean ignoreDelay)
  998.     {
  999.         delayTimer.stop();
  1000.  
  1001.  
  1002.         if ((scrollState == DONE_SCROLLING_OUT && newScrollState == SCROLLING_IN)
  1003.             || (scrollState == WAITING_TO_SCROLL_IN && newScrollState == SCROLLING_IN))
  1004.         {
  1005.             //Start scrolling in!
  1006.             //Start the animation or the timer, whichever is appropriate
  1007.             if (ignoreDelay || textDelay == 0)
  1008.             {
  1009.                 scrollState = newScrollState;
  1010.  
  1011.                 textAnimationThread = new Thread(this);
  1012.                 textAnimationThread.start();
  1013.             }
  1014.             else
  1015.             {
  1016.                 scrollState = WAITING_TO_SCROLL_IN;
  1017.                 delayTimer.start();
  1018.             }
  1019.         }
  1020.         else if (scrollState == DONE_SCROLLING_IN && newScrollState == SCROLLING_OUT)
  1021.         {
  1022.             //Start scrolling out!
  1023.             scrollState = newScrollState;
  1024.  
  1025.             textAnimationThread = new Thread(this);
  1026.             textAnimationThread.start();
  1027.         }
  1028.         else if (scrollState == WAITING_TO_SCROLL_IN && newScrollState == SCROLLING_OUT)
  1029.         {
  1030.             //We never actually started the scrolling in animation before this mouseExited event
  1031.             //occurred, so all we have to do is set the scrollState to DONE_SCROLLING_OUT
  1032.             scrollState = DONE_SCROLLING_OUT;
  1033.         }
  1034.         else
  1035.         {
  1036.             //We should already be in the process of scrolling text one way or the other.  Just
  1037.             //update scrollState, and the animation thread will take care of the rest.
  1038.             scrollState = newScrollState;
  1039.         }
  1040.     }
  1041.  
  1042.     /**
  1043.      * Stops the scrolling text animation (if any).  Any subsequent mouse
  1044.      * over events will cause the text to scroll again, as usual.
  1045.      *
  1046.      * @see #startScrolling
  1047.      */
  1048.     protected void stopScrolling()
  1049.     {
  1050.         delayTimer.stop();    //these two lines stop any pending scrolls
  1051.  
  1052.         if (scrollState != DONE_SCROLLING_IN)
  1053.             scrollState = DONE_SCROLLING_OUT;    //It's OK if the text is already done scrolling in.  Otherwise,
  1054.                                                 //set the state to DONE_SCROLLING_OUT
  1055.         if (textAnimationThread != null)
  1056.         {
  1057.             textAnimationThread.stop();
  1058.             textAnimationThread = null;
  1059.             repaint();  //repaint to get rid of text which may be partially scrolled in
  1060.         }
  1061.     }
  1062.  
  1063.     /**
  1064.      * Draws the text next to the component (in the location specified
  1065.      * by textLocation).
  1066.      *
  1067.      * @see #popDownText
  1068.      */
  1069.     protected void popUpText()
  1070.     {
  1071.         //with this one call we draw the text, and also update the scrollState.
  1072.         drawNextFrame(getTextFinishingPoint());
  1073.     }
  1074.  
  1075.     /**
  1076.      * Basically just erases the text next to the component (in the location specified
  1077.      * by textLocation).
  1078.      *
  1079.      * @see #popUpText
  1080.      */
  1081.     protected void popDownText()
  1082.     {
  1083.         Graphics g = getGraphics();
  1084.  
  1085.         if (g != null)
  1086.         {
  1087.             Rectangle bufferedBgndImageBounds = getBufferedBackgroundBounds();
  1088.  
  1089.             if (bufferedBgndImageBounds != null)
  1090.             {
  1091.                 //just paint our buffered background
  1092.                 g.drawImage(bufferedBgndImage, bufferedBgndImageBounds.x, bufferedBgndImageBounds.y, this);
  1093.  
  1094.                 //all done!  Set our scrollState accordingly.
  1095.                 scrollState = DONE_SCROLLING_OUT;
  1096.             }
  1097.         }
  1098.     }
  1099.  
  1100.     /**
  1101.      * Start the text scrolling in or out from the appropriate location.
  1102.      * The text will continue to be scrolled back and forth until the text
  1103.      * scrolls all the way in, or all the way out.
  1104.      */
  1105.     protected void scrollText()
  1106.     {
  1107.         Point currentTextLocation;
  1108.         if (scrollState == SCROLLING_IN)
  1109.             currentTextLocation = getTextStartingPoint();
  1110.         else
  1111.             currentTextLocation = getTextFinishingPoint();
  1112.  
  1113.         while (scrollState != DONE_SCROLLING_IN && scrollState != DONE_SCROLLING_OUT)
  1114.             currentTextLocation = scrollOneDirection(currentTextLocation);
  1115.     }
  1116.  
  1117.     /**
  1118.      * Utility method of run
  1119.      *
  1120.      * This method checks to see whether the off screen images necessary for
  1121.      * the animation already exist, and if so that they are of the correct size.
  1122.      * If the buffers do not exist this method tries to create them.
  1123.      *
  1124.      * Due to a bug in some VMs componentResize events may not get sent correctly
  1125.      * so we must also check that the sizes of the buffers are correct.
  1126.      *
  1127.      * @return a boolean indicating whether or not buffers exist by the end of
  1128.      * this method which are sufficient for the animation.
  1129.      * @see #run
  1130.      */
  1131.     protected boolean validateOffScreenBuffers()
  1132.     {
  1133.  
  1134.         if (offScreenImage == null || bufferedBgndImage == null)
  1135.         {
  1136.             createOffScreenBuffers();
  1137.         }
  1138.         else
  1139.         {
  1140.             //Since component resize events are not sent correctly on some VMs,
  1141.             //we must check to see if the component size is in sync with the
  1142.             //off screen buffer sizes.  Once this works in all VMs we can accomplish
  1143.             //this through listening to component resize events.
  1144.  
  1145.             Rectangle correctBounds = getBufferedBackgroundBounds();
  1146.  
  1147.             if (correctBounds != null)
  1148.             {
  1149.                 if (offScreenImage.getWidth(this) != correctBounds.width
  1150.                     || offScreenImage.getHeight(this) != correctBounds.height)
  1151.                 {
  1152.                     createOffScreenBuffers();
  1153.                 }
  1154.             }
  1155.         }
  1156.  
  1157.         return (offScreenImage != null && bufferedBgndImage != null);
  1158.     }
  1159.  
  1160.     /**
  1161.      * Utility method for run.
  1162.      *
  1163.      * This method stores the background (in this case only the panel's image
  1164.      * since this is a heavyweight component) into bufferedBgndImage.
  1165.      * Only the portion of the background inside of the bounds specified by
  1166.      * getBufferedBackgroundBounds() is saved.
  1167.      *
  1168.      * @see #run
  1169.      */
  1170.     protected void storeBackground()
  1171.     {
  1172.         if (bufferedBgndImage != null)
  1173.         {
  1174.             Graphics buffer = bufferedBgndImage.getGraphics();
  1175.             Rectangle bufferBounds = getBufferedBackgroundBounds();
  1176.  
  1177.             if (buffer != null && bufferBounds != null)
  1178.             {
  1179.                 buffer.translate(-bufferBounds.x, -bufferBounds.y);
  1180.  
  1181.                 dontDrawText = true;
  1182.                 try
  1183.                 {
  1184.                     paint(buffer);
  1185.                 }
  1186.                 finally
  1187.                 {
  1188.                     dontDrawText = false;
  1189.                 }
  1190.  
  1191.                 buffer.translate(bufferBounds.x, bufferBounds.y);
  1192.             }
  1193.         }
  1194.     }
  1195.  
  1196.     /**
  1197.      * Starts the text scrolling in the current direction, starting from
  1198.      * the point specified by the parameter startingPoint.
  1199.      * The text will continue to be scrolled until the current direction
  1200.      * changes (due to a mouse event, or due to the text scrolling all
  1201.      * the way in or out).
  1202.      *
  1203.      * @param startingPoint the point from which the text starts scrolling
  1204.      * @return the point at which the text stopped scrolling
  1205.      */
  1206.     protected Point scrollOneDirection(Point startingPoint)
  1207.     {
  1208.         Point currentPoint = startingPoint;
  1209.         int currentDirection = scrollState;
  1210.  
  1211.         while(scrollState == currentDirection)
  1212.         {
  1213.             try
  1214.             {
  1215.                 textAnimationThread.sleep(TIME_BETWEEN_FRAMES);
  1216.             }
  1217.             catch (InterruptedException e) {}
  1218.  
  1219.             drawNextFrame(currentPoint);
  1220.         }
  1221.  
  1222.         return currentPoint;
  1223.     }
  1224.  
  1225.     /**
  1226.      * Does all necessary calculations and graphics work to advance the text one frame
  1227.      * (starting from the point specified in the paramter startingPoint).
  1228.      *
  1229.      * statingPoint is modified by this method to be the NEW position of the text (scrolled
  1230.      * one frame further than when the method was called)
  1231.      *
  1232.      * @param startingPoint the current position of the text (before this method advances it
  1233.      * by one animation frame).
  1234.         */
  1235.     protected void drawNextFrame(Point startingPoint)
  1236.     {
  1237.         Graphics g = getGraphics();
  1238.  
  1239.         if (g != null)
  1240.         {
  1241.             Graphics offScreenGraphics = offScreenImage.getGraphics();
  1242.  
  1243.             Rectangle bufferedBgndImageBounds = getBufferedBackgroundBounds();
  1244.  
  1245.             if (bufferedBgndImageBounds != null)
  1246.             {
  1247.                 //first, paint our buffered background
  1248.                 offScreenGraphics.drawImage(bufferedBgndImage, 0, 0, this);
  1249.  
  1250.                 //now, paint our text on top of that
  1251.                 startingPoint.x += calculateDX(startingPoint);
  1252.                 startingPoint.y += calculateDY(startingPoint);
  1253.  
  1254.                 drawText(offScreenGraphics, startingPoint.x - bufferedBgndImageBounds.x, startingPoint.y - bufferedBgndImageBounds.y);
  1255.  
  1256.                 //take the resulting off screen image and paint it into our 'on-screen' graphics context
  1257.                 g.drawImage(offScreenImage, bufferedBgndImageBounds.x, bufferedBgndImageBounds.y, this);
  1258.  
  1259.                 //determine what direction we should be scrolling in now
  1260.                 updateDirection(startingPoint);
  1261.             }
  1262.         }
  1263.     }
  1264.  
  1265.     /**
  1266.      * Utility method for drawNextFrame.
  1267.      *
  1268.      * Given the current location of the text, this method calculates how far in the
  1269.      * x-direction the text should move when it advances to the next frame of the animation.
  1270.      *
  1271.      * @param startingPoint the current location of the text.
  1272.      * @return the distance in the x-direction the text should move when it advances
  1273.      * to the next frame of the animation.
  1274.      * @see #calculateDY
  1275.      */
  1276.     protected int calculateDX(Point startingPoint)
  1277.     {
  1278.         int dx = 0;
  1279.         Point destination = new Point();
  1280.  
  1281.         if (textLocation == ABOVE_COMPONENT || textLocation == BELOW_COMPONENT)
  1282.             return 0;
  1283.  
  1284.         switch (scrollState)
  1285.         {
  1286.             case SCROLLING_IN:
  1287.                 destination = getTextFinishingPoint();
  1288.  
  1289.                 if (atOrBeyondPoint(startingPoint, destination, DX_PER_FRAME))
  1290.                     dx = -(startingPoint.x - destination.x);
  1291.                  else
  1292.                     dx = (textLocation == LEFT_OF_COMPONENT ? DX_PER_FRAME : -DX_PER_FRAME);
  1293.                 break;
  1294.             case SCROLLING_OUT:
  1295.                 destination = getTextStartingPoint();
  1296.  
  1297.                 if (atOrBeyondPoint(startingPoint, destination, DX_PER_FRAME))
  1298.                     dx = (destination.x - startingPoint.x);
  1299.                 else
  1300.                     dx = (textLocation == LEFT_OF_COMPONENT ? -DX_PER_FRAME : DX_PER_FRAME);
  1301.                 break;
  1302.             case DONE_SCROLLING_OUT:
  1303.             case DONE_SCROLLING_IN:
  1304.                 dx = 0;
  1305.                 break;
  1306.         }
  1307.         return dx;
  1308.     }
  1309.  
  1310.     /**
  1311.      * Utility method for drawNextFrame.
  1312.      *
  1313.      * Given the current location of the text, this method calculates how far in the
  1314.      * y-direction the text should move when it advances to the next frame of the animation.
  1315.      *
  1316.      * @param startingPoint the current location of the text.
  1317.      * @return the distance in the y-direction the text should move when it advances
  1318.      * to the next frame of the animation.
  1319.      * @see #calculateDX
  1320.      */
  1321.     protected int calculateDY(Point startingPoint)
  1322.     {
  1323.         int dy = 0;
  1324.         Point destination;
  1325.  
  1326.         if (textLocation == RIGHT_OF_COMPONENT || textLocation == LEFT_OF_COMPONENT)
  1327.             return 0;
  1328.  
  1329.         switch (scrollState)
  1330.         {
  1331.             case SCROLLING_IN:
  1332.                 destination = getTextFinishingPoint();
  1333.  
  1334.                 if (atOrBeyondPoint(startingPoint, destination, DY_PER_FRAME))
  1335.                     dy = -(startingPoint.y - destination.y);
  1336.                  else
  1337.                     dy = (textLocation == BELOW_COMPONENT ? -DY_PER_FRAME : DY_PER_FRAME);
  1338.                 break;
  1339.             case SCROLLING_OUT:
  1340.                 destination = getTextStartingPoint();
  1341.  
  1342.                 if (atOrBeyondPoint(startingPoint, destination, DY_PER_FRAME))
  1343.                     dy = (destination.y - startingPoint.y);
  1344.                 else
  1345.                     dy = (textLocation == BELOW_COMPONENT ? DY_PER_FRAME : -DY_PER_FRAME);
  1346.                 break;
  1347.             case DONE_SCROLLING_OUT:
  1348.             case DONE_SCROLLING_IN:
  1349.                 dy = 0;
  1350.                 break;
  1351.         }
  1352.         return dy;
  1353.     }
  1354.  
  1355.     /**
  1356.      * Utility method for drawNextFrame.
  1357.      *
  1358.      * Given the current location of the text, this method determines whether the
  1359.      * scrollState should be set to SCROLLING_IN, SCROLLING_OUT, DONE_SCROLLING_IN, or
  1360.      * DONE_SCROLLING_OUT.
  1361.      * This method actually modifies the 'scrollState' member of this class.
  1362.      *
  1363.      * @see #drawNextFrame
  1364.      */
  1365.     protected void updateDirection(Point currentPoint)
  1366.     {
  1367.         switch (scrollState)
  1368.         {
  1369.             case SCROLLING_IN:
  1370.                 if (atOrBeyondPoint(currentPoint, getTextFinishingPoint(), 0))
  1371.                     scrollState = DONE_SCROLLING_IN;
  1372.                 break;
  1373.             case SCROLLING_OUT:
  1374.                 if (atOrBeyondPoint(currentPoint, getTextStartingPoint(), 0))
  1375.                     scrollState = DONE_SCROLLING_OUT;
  1376.                 break;
  1377.             case DONE_SCROLLING_OUT:
  1378.             case DONE_SCROLLING_IN:
  1379.                 break;
  1380.         }
  1381.     }
  1382.  
  1383.     /**
  1384.      * Utility method for updateDirection, calculateDX, and calculateDY.
  1385.      *
  1386.      * If paramter 'allowance' equals zero, this method determines whether or not the text
  1387.      * has scrolled to a position which is at or beyond the point specified in the
  1388.      * parameter 'point' (based on textLocation and scrollState).
  1389.      *
  1390.      * For a non-zero 'allowance', this method determines whether or not the text
  1391.      * has scrolled to a position which is either beyond 'point' or within 'allowance'
  1392.      * pixels of 'point' (based on the current textLocation and scrollState).
  1393.      *
  1394.      * @return whether or not 'currentLocation' is at or beyond 'point' (given 'allowance' pixels of leeway).
  1395.      */
  1396.     protected boolean atOrBeyondPoint(Point currentLocation, Point point, int allowance)
  1397.     {
  1398.         if (currentLocation == null || point == null)
  1399.             return false;
  1400.  
  1401.         switch (textLocation)
  1402.         {
  1403.             case RIGHT_OF_COMPONENT:
  1404.                 switch (scrollState)
  1405.                 {
  1406.                      case SCROLLING_IN:
  1407.                         return currentLocation.x <= point.x + allowance;
  1408.                     case SCROLLING_OUT:
  1409.                         return currentLocation.x >= point.x - allowance;
  1410.                 }
  1411.             case LEFT_OF_COMPONENT:
  1412.                 switch (scrollState)
  1413.                 {
  1414.                      case SCROLLING_IN:
  1415.                         return currentLocation.x >= point.x - allowance;
  1416.                     case SCROLLING_OUT:
  1417.                         return currentLocation.x <= point.x + allowance;
  1418.                 }
  1419.             case ABOVE_COMPONENT:
  1420.                 switch (scrollState)
  1421.                 {
  1422.                      case SCROLLING_IN:
  1423.                         return currentLocation.y >= point.y - allowance;
  1424.                     case SCROLLING_OUT:
  1425.                         return currentLocation.y <= point.y + allowance;
  1426.                 }
  1427.             case BELOW_COMPONENT:
  1428.                 switch (scrollState)
  1429.                 {
  1430.                      case SCROLLING_IN:
  1431.                         return currentLocation.y <= point.y + allowance;
  1432.                     case SCROLLING_OUT:
  1433.                         return currentLocation.y >= point.y - allowance;
  1434.                 }
  1435.         }
  1436.         return false;
  1437.     }
  1438.  
  1439.     /**
  1440.      * Utility method for getTextStartingPoint and getTextFinishingPoint.
  1441.      *
  1442.      * This method calculates the y-position at which the text should be drawn if
  1443.      * the textLocation is either LEFT_OF_COMPONENT or RIGHT_OF_COMPONENT.  Requires
  1444.      * a graphics context to be available.
  1445.      *
  1446.      * @return the y-position at which the text should be drawn if
  1447.      * the textLocation is either LEFT_OF_COMPONENT or RIGHT_OF_COMPONENT.
  1448.      * @see #getTextStartingPoint
  1449.      * @see #getTextFinishingPoint
  1450.      */
  1451.     protected int getTextConstantYPos()
  1452.     {
  1453.         return myComponent.getLocation().y + (myComponent.getSize().height + textAscent)/2;
  1454.     }
  1455.  
  1456.     /**
  1457.      * Utility method for getTextStartingPoint and getTextFinishingPoint.
  1458.      *
  1459.      * This method calculates the x-position at which the text should be drawn if
  1460.      * the textLocation is either ABOVE_COMPONENT or BELOW_COMPONENT.  Requires
  1461.      * a graphics context to be available.
  1462.      *
  1463.      * @return the x-position at which the text should be drawn if
  1464.      * the textLocation is either ABOVE_COMPONENT or BELOW_COMPONENT.
  1465.      * @see #getTextStartingPoint
  1466.      * @see #getTextFinishingPoint
  1467.      */
  1468.     protected int getTextConstantXPos()
  1469.     {
  1470.         return myComponent.getLocation().x + (myComponent.getSize().width - textWidth) / 2;
  1471.     }
  1472.  
  1473.     /**
  1474.      * Utility method.
  1475.      *
  1476.      * This method calculates the ascent, descent, height, and width of the current
  1477.      * text, based on the current graphics context.
  1478.      *
  1479.      */
  1480.     protected void calculateTextAttributes()
  1481.     {
  1482.         if (text == null)
  1483.             return;
  1484.  
  1485.         Graphics g = getGraphics();
  1486.         if (g != null)
  1487.         {
  1488.             FontMetrics fm    = g.getFontMetrics();
  1489.             if (fm != null)
  1490.             {
  1491.                 textAscent = fm.getAscent();
  1492.                 textDescent = fm.getDescent();
  1493.                 textHeight = textAscent + textDescent;
  1494.                 textWidth = fm.stringWidth(text);
  1495.             }
  1496.         }
  1497.     }
  1498.  
  1499.     /**
  1500.      * Calculates the rectangle which bounds the portion of this component's background which
  1501.      * the text will animate over.
  1502.      *
  1503.      * @return this bounding rectangle.
  1504.      */
  1505.     protected Rectangle getBufferedBackgroundBounds()
  1506.     {
  1507.         if (myComponent != null)
  1508.         {
  1509.             if (textScrollsIn)
  1510.                 return getAreaAffectedByScroll();
  1511.             else
  1512.                 return getAreaAffectedByPopup();
  1513.         }
  1514.  
  1515.         //The component has not yet been set, so return null
  1516.         return null;
  1517.     }
  1518.  
  1519.     /**
  1520.      * Utility method for getBufferedBackgroundBounds.
  1521.      *
  1522.      * Calculates the rectangle which bounds the portion of this component's background
  1523.      * which the text will scroll over.
  1524.      *
  1525.      * @return this bounding rectangle (or null if there is insufficient space to do the
  1526.      * animation.
  1527.      */
  1528.     protected Rectangle getAreaAffectedByScroll()
  1529.     {
  1530.         int leftMostX = getBufferedBackgroundLeftMostX();
  1531.         int rightMostX = getBufferedBackgroundRightMostX();
  1532.  
  1533.         int topMostY = getBufferedBackgroundTopMostY();
  1534.         int bottomMostY = getBufferedBackgroundBottomMostY();
  1535.  
  1536.         if (rightMostX > leftMostX && topMostY < bottomMostY)
  1537.         {
  1538.             return new Rectangle(leftMostX - 1, topMostY - 1,
  1539.                                     rightMostX - leftMostX + 1, bottomMostY - topMostY + 1);
  1540.                                     // +1 and -1 to allow for drawing a rectangle around the text.
  1541.         }
  1542.  
  1543.         //The InfoTipPanel is not large enough for the user to
  1544.         //see any animation, so return null
  1545.         return null;
  1546.     }
  1547.  
  1548.     /**
  1549.      * Utility method for getBufferedBackgroundBounds.
  1550.      *
  1551.      * Calculates the rectangle which bounds the portion of this component's background
  1552.      * which the text will pop-up over.
  1553.      *
  1554.      * @return this bounding rectangle (or null if there is insufficient space to do the
  1555.      * animation.
  1556.      */
  1557.     protected Rectangle getAreaAffectedByPopup()
  1558.     {
  1559.         int leftMostX, rightMostX, topMostY, bottomMostY;
  1560.  
  1561.         if (textLocation == RIGHT_OF_COMPONENT)
  1562.             rightMostX = Math.min(getSize().width, getTextFinishingPoint().x + textWidth + (highlightText ? extraHighlightWidth/2 : 0));
  1563.         else
  1564.             rightMostX = getBufferedBackgroundRightMostX();
  1565.  
  1566.         if (textLocation == LEFT_OF_COMPONENT)
  1567.             leftMostX = Math.max(0, getTextFinishingPoint().x - (highlightText ? extraHighlightWidth/2 : 0));
  1568.         else
  1569.             leftMostX = getBufferedBackgroundLeftMostX();
  1570.  
  1571.         if (textLocation == ABOVE_COMPONENT)
  1572.             topMostY = Math.max(0, getTextFinishingPoint().y - textAscent - (highlightText ? extraHighlightHeight/2 : 0));
  1573.         else
  1574.             topMostY = getBufferedBackgroundTopMostY();
  1575.  
  1576.         if (textLocation == BELOW_COMPONENT)
  1577.             bottomMostY = Math.min(getSize().height, getTextFinishingPoint().y + textDescent + (highlightText ? extraHighlightHeight/2 : 0));
  1578.         else
  1579.             bottomMostY = getBufferedBackgroundBottomMostY();
  1580.  
  1581.  
  1582.  
  1583.         if (rightMostX > leftMostX && topMostY < bottomMostY)
  1584.         {
  1585.             return new Rectangle(leftMostX - 1, topMostY - 1,
  1586.                                     rightMostX - leftMostX + 1, bottomMostY - topMostY + 1);
  1587.                                     // +1 and -1 to allow for drawing a rectangle around the text.
  1588.         }
  1589.  
  1590.         //The InfoTipPanel is not large enough for the user to
  1591.         //see any animation, so return null
  1592.         return null;
  1593.     }
  1594.  
  1595.     /**
  1596.      * Utility method for getBufferedBackgroundBounds.
  1597.      *
  1598.      * This method calculates the y-coordinate of the rectangle which bounds the portion
  1599.      * of this component's background which the text will animate over.
  1600.      *
  1601.      * @return this y-coordinate.
  1602.      * @see #getBufferedBackgroundBounds
  1603.      * @see #getBufferedBackgroundBottomMostY
  1604.      * @see #getBufferedBackgroundLeftMostX
  1605.      * @see #getBufferedBackgroundRightMostX
  1606.      */
  1607.     protected int getBufferedBackgroundTopMostY()
  1608.     {
  1609.         int topMostY = 0;
  1610.  
  1611.         switch (textLocation)
  1612.         {
  1613.             case RIGHT_OF_COMPONENT:
  1614.             case LEFT_OF_COMPONENT:
  1615.                 topMostY = getTextConstantYPos() - textAscent - (highlightText ? extraHighlightHeight/2 : 0);
  1616.                 break;
  1617.             case ABOVE_COMPONENT:
  1618.                 topMostY = 0;
  1619.                 break;
  1620.             case BELOW_COMPONENT:
  1621.                 topMostY = (myComponent.getLocation().y + myComponent.getSize().height + PIXEL_BUFFER  - (highlightText ? extraHighlightHeight/2 : 0));
  1622.                 break;
  1623.         }
  1624.  
  1625.         return topMostY;
  1626.     }
  1627.  
  1628.     /**
  1629.      * Utility method for getBufferedBackgroundBounds.
  1630.      *
  1631.      * This method calculates the y-coordinate of the bottom edge of the rectangle
  1632.      * which bounds the portion of this component's background which the text will
  1633.      * animate over.
  1634.      *
  1635.      * @return this y-coordinate.
  1636.      * @see #getBufferedBackgroundBounds
  1637.      * @see #getBufferedBackgroundTopMostY
  1638.      * @see #getBufferedBackgroundLeftMostX
  1639.      * @see #getBufferedBackgroundRightMostX
  1640.      */
  1641.     protected int getBufferedBackgroundBottomMostY()
  1642.     {
  1643.         int bottomMostY = 0;
  1644.  
  1645.         switch (textLocation)
  1646.         {
  1647.             case RIGHT_OF_COMPONENT:
  1648.             case LEFT_OF_COMPONENT:
  1649.                 bottomMostY = getTextConstantYPos() + textDescent + (highlightText ? extraHighlightHeight/2 : 0);
  1650.                 break;
  1651.             case ABOVE_COMPONENT:
  1652.                 bottomMostY = myComponent.getLocation().y - PIXEL_BUFFER + textDescent + (highlightText ? extraHighlightHeight/2 : 0);
  1653.                 break;
  1654.             case BELOW_COMPONENT:
  1655.                 bottomMostY = getSize().height;
  1656.                 break;
  1657.         }
  1658.  
  1659.         return bottomMostY;
  1660.     }
  1661.  
  1662.     /**
  1663.      * Utility method for getBufferedBackgroundBounds.
  1664.      *
  1665.      * This method calculates the x-coordinate of the rectangle which bounds
  1666.      * the portion of this component's background which the text will animate over.
  1667.      *
  1668.      * @return this x-coordinate.
  1669.      * @see #getBufferedBackgroundBounds
  1670.      * @see #getBufferedBackgroundTopMostY
  1671.      * @see #getBufferedBackgroundBottomMostY
  1672.      * @see #getBufferedBackgroundRightMostX
  1673.      */
  1674.     protected int getBufferedBackgroundLeftMostX()
  1675.     {
  1676.         int xPos = 0;
  1677.  
  1678.         switch (textLocation)
  1679.         {
  1680.             case RIGHT_OF_COMPONENT:
  1681.                 xPos = getTextFinishingPoint().x - (highlightText ? extraHighlightWidth/2 : 0);
  1682.                 break;
  1683.             case LEFT_OF_COMPONENT:
  1684.                 xPos = 0;
  1685.                 break;
  1686.             case ABOVE_COMPONENT:
  1687.             case BELOW_COMPONENT:
  1688.                 xPos = getTextConstantXPos() - (highlightText ? extraHighlightWidth/2 : 0);
  1689.                 break;
  1690.         }
  1691.  
  1692.         return xPos;
  1693.     }
  1694.  
  1695.     /**
  1696.      * Utility method for getBufferedBackgroundBounds.
  1697.      *
  1698.      * This method calculates the x-coordinate of the right edge of the rectangle
  1699.      * which bounds the portion of this component's background which the text will
  1700.      * animate over.
  1701.      *
  1702.      * @return this x-coordinate.
  1703.      * @see #getBufferedBackgroundBounds
  1704.      * @see #getBufferedBackgroundTopMostY
  1705.      * @see #getBufferedBackgroundBottomMostY
  1706.      * @see #getBufferedBackgroundLeftMostX
  1707.      */
  1708.     protected int getBufferedBackgroundRightMostX()
  1709.     {
  1710.         int xPos = 0;
  1711.  
  1712.         switch (textLocation)
  1713.         {
  1714.             case RIGHT_OF_COMPONENT:
  1715.                 xPos = getSize().width;
  1716.                 break;
  1717.             case LEFT_OF_COMPONENT:
  1718.                 xPos = myComponent.getLocation().x - PIXEL_BUFFER + (highlightText ? extraHighlightWidth/2 : 0);
  1719.                 break;
  1720.             case ABOVE_COMPONENT:
  1721.             case BELOW_COMPONENT:
  1722.                 xPos = getTextConstantXPos() + textWidth + (highlightText ? extraHighlightWidth/2 : 0);
  1723.                 break;
  1724.         }
  1725.  
  1726.         return xPos;
  1727.     }
  1728.  
  1729.     /**
  1730.      * Calculates the point at which the text should be located when the scrolling
  1731.      * animation is initiated.
  1732.      *
  1733.      * @return this Point
  1734.      * @see #getTextFinishingPoint
  1735.      */
  1736.     protected Point getTextStartingPoint()
  1737.     {
  1738.         Point startingPoint = new Point(0,0);
  1739.  
  1740.         switch (textLocation)
  1741.         {
  1742.             case RIGHT_OF_COMPONENT:
  1743.                 startingPoint.x = getSize().width + (highlightText ? extraHighlightWidth/2 : 0);
  1744.                 startingPoint.y = getTextConstantYPos();
  1745.                 break;
  1746.             case LEFT_OF_COMPONENT:
  1747.                 startingPoint.x = -textWidth - (highlightText ? extraHighlightWidth/2 : 0);
  1748.                 startingPoint.y = getTextConstantYPos();
  1749.                 break;
  1750.             case ABOVE_COMPONENT:
  1751.                 startingPoint.x = getTextConstantXPos();
  1752.                 startingPoint.y = -textDescent -  (highlightText ? extraHighlightHeight/2 : 0);
  1753.                 break;
  1754.             case BELOW_COMPONENT:
  1755.                 startingPoint.x = getTextConstantXPos();
  1756.                 startingPoint.y = getSize().height + textAscent + (highlightText ? extraHighlightHeight/2 : 0);
  1757.                 break;
  1758.         }
  1759.         return startingPoint;
  1760.     }
  1761.  
  1762.     /**
  1763.      * Calculates the point at which the text should be located when the scrolling
  1764.      * animation has scrolled the text all the way in.
  1765.      *
  1766.      * @return this Point
  1767.      * @see #getTextStartingPoint
  1768.      */
  1769.     protected Point getTextFinishingPoint()
  1770.     {
  1771.         Point finishingPoint = new Point(0,0);
  1772.  
  1773.         switch (textLocation)
  1774.         {
  1775.             case RIGHT_OF_COMPONENT:
  1776.                 finishingPoint.x = myComponent.getLocation().x + myComponent.getSize().width + PIXEL_BUFFER;
  1777.                 finishingPoint.y = getTextConstantYPos();
  1778.                 break;
  1779.             case LEFT_OF_COMPONENT:
  1780.                 finishingPoint.x = myComponent.getLocation().x - PIXEL_BUFFER - textWidth;
  1781.                 finishingPoint.y = getTextConstantYPos();
  1782.                 break;
  1783.             case ABOVE_COMPONENT:
  1784.                 finishingPoint.x = getTextConstantXPos();
  1785.                 finishingPoint.y = myComponent.getLocation().y - PIXEL_BUFFER;
  1786.                 break;
  1787.             case BELOW_COMPONENT:
  1788.                 finishingPoint.x = getTextConstantXPos();
  1789.                 finishingPoint.y = myComponent.getLocation().y + myComponent.getSize().height + PIXEL_BUFFER + textAscent;
  1790.                 break;
  1791.         }
  1792.         return finishingPoint;
  1793.     }
  1794.  
  1795.     /**
  1796.      * Adds the specified component to this container at the specified
  1797.      * index.  Also notifies the layout manager to add the component to
  1798.      * the this container's layout using the specified constraints object.
  1799.      * <p>
  1800.      * This is the method to override if you want to track every add
  1801.      * request to a container.  An overriding method should usually
  1802.      * include a call to super.addImpl(comp, constraints, index).
  1803.      *
  1804.      * It is overridden here to set the only
  1805.      * component in this InfoTipPanel. This is the component
  1806.      * that triggers text scrolling on a mouse entered event.
  1807.      * The InfoTipPanel can only contain one component.  Any
  1808.      * previous component will be removed before the new one is added.
  1809.      *
  1810.      * @param comp the component to be added
  1811.      * @param constraints an object expressing layout contraints for this
  1812.      * component
  1813.      * @param index the position in the container's list at which to
  1814.      * insert the component.  -1 means insert at the end.
  1815.      * @see #remove
  1816.      */
  1817.     protected void addImpl(Component comp, Object constraints, int index)
  1818.     {
  1819.         if (callingAddInternally)
  1820.         {
  1821.             //if we are calling add from Within InfoTipPanel do a regular old addImpl()
  1822.             super.addImpl(comp,constraints,index);
  1823.             return;
  1824.         }
  1825.  
  1826.         setComponent(comp);
  1827.     }
  1828.  
  1829.      /**
  1830.      * This method re-creates the off screen images necessary for the text animation.
  1831.      * This method must be called any time that the sizes of these images must be
  1832.      * re-calculated -- it will flush() the old images (if any) and create new images
  1833.      * of the correct size.
  1834.      */
  1835.     protected void createOffScreenBuffers()
  1836.     {
  1837.         if (bufferedBgndImage != null)
  1838.         {
  1839.             bufferedBgndImage.flush();
  1840.             bufferedBgndImage = null;
  1841.         }
  1842.  
  1843.         if (offScreenImage != null)
  1844.         {
  1845.             offScreenImage.flush();
  1846.             offScreenImage = null;
  1847.         }
  1848.  
  1849.         Rectangle bufferedBgndImageBounds = getBufferedBackgroundBounds();
  1850.         if (bufferedBgndImageBounds != null)
  1851.         {
  1852.             bufferedBgndImage = createImage(bufferedBgndImageBounds.width, bufferedBgndImageBounds.height);
  1853.             offScreenImage = createImage(bufferedBgndImageBounds.width, bufferedBgndImageBounds.height);
  1854.         }
  1855.  
  1856.         storeBackground();
  1857.     }
  1858.  
  1859.     /**
  1860.      * Standard method which handles serializing this object.
  1861.      * It has been OVERRIDDEN here to re-calculate all of the
  1862.      * state which is marked transient (including scrollState,
  1863.      * sizes, and offScreenBuffers).
  1864.      */
  1865.     private void readObject(java.io.ObjectInputStream in)
  1866.         throws IOException, ClassNotFoundException
  1867.     {
  1868.         in.defaultReadObject();
  1869.  
  1870.         scrollState = DONE_SCROLLING_OUT;
  1871.  
  1872.         calculateTextAttributes();
  1873.  
  1874.         createOffScreenBuffers();
  1875.     }
  1876.  
  1877.  
  1878.     /**
  1879.      * This internal variable represents the state of the current text-scrolling
  1880.      * animation, if any.
  1881.      * Should be one of: SCROLLING_IN, DONE_SCROLLING_IN, SCROLLING_OUT,
  1882.      * or DONE_SCROLLING_OUT.
  1883.      */
  1884.     transient int scrollState;
  1885.  
  1886.     /**
  1887.      * This property represents the direction from which the text should be
  1888.      * scrolled by the InfoTipPanel.
  1889.      * Should be one of: RIGHT_OF_COMPONENT, LEFT_OF_COMPONENT, ABOVE_COMPONENT,
  1890.      * or BELOW_COMPONENT.
  1891.      * @see #setTextLocation
  1892.      * @see #getTextLocation
  1893.      */
  1894.     int textLocation;
  1895.  
  1896.     /**
  1897.       * The distance between the edge of the added component and the point at which the text stops scrolling.
  1898.       * @see #getTextFinishingPoint
  1899.       */
  1900.     static final int PIXEL_BUFFER = 10;
  1901.  
  1902.     /**
  1903.       * The horizontal distance which the text moves each animation frame (if the text is scrolling left or right).
  1904.       * @see #drawNextFrame
  1905.       */
  1906.     static final int DX_PER_FRAME = 25;
  1907.  
  1908.     /**
  1909.       * The vertical distance which the text moves each animation frame (if the text is scrolling up or down).
  1910.       * @see #drawNextFrame
  1911.       */
  1912.     static final int DY_PER_FRAME = 17;
  1913.  
  1914.     /**
  1915.       * The time (in milliseconds) paused between each animation frame.
  1916.       * @see #scrollOneDirection
  1917.       */
  1918.     static final int TIME_BETWEEN_FRAMES = 50;
  1919.  
  1920.     /**
  1921.      * The thread which will animate the scrolling text.
  1922.      */
  1923.     transient protected Thread textAnimationThread = null;
  1924.     /**
  1925.      * Off screen buffer used for animation.
  1926.      */
  1927.     transient protected Image offScreenImage = null;
  1928.     /**
  1929.      * Off screen buffer used for animation.
  1930.      */
  1931.     transient protected Image bufferedBgndImage = null;
  1932.  
  1933.     /**
  1934.      * Internal flag that indicates a call to add() is being made from within InfoTipPanel.
  1935.      */
  1936.     transient protected boolean    callingAddInternally = false;
  1937.  
  1938.     /**
  1939.      * Internal flag that indicates that the component is NOT to draw the text from within paint().
  1940.      */
  1941.     transient protected boolean    dontDrawText = false;
  1942.  
  1943.     /**
  1944.      * Width of current message text.
  1945.      */
  1946.     transient protected int textWidth;
  1947.  
  1948.     /**
  1949.      * Height of current message text.
  1950.      */
  1951.     transient protected int textHeight;
  1952.  
  1953.     /**
  1954.      * Ascent of current message text.
  1955.      */
  1956.     transient protected int textAscent;
  1957.  
  1958.     /**
  1959.      * Descent of current message text.
  1960.      */
  1961.     transient protected int textDescent;
  1962.  
  1963.     /**
  1964.      * Reference to the component which will be added to the InfoTipPanel and
  1965.      * whose mouse entered and mouse exited events will control the scrolling of the text.
  1966.      * @see #setComponent
  1967.      * @see #getComponent
  1968.      */
  1969.     protected Component myComponent = null;
  1970.  
  1971.     /**
  1972.      * The text which will be scrolled on mouse entered and mouse exited events from myComponent.
  1973.      * @see #setText
  1974.      * @see #getText
  1975.      */
  1976.     protected String text = null;
  1977.  
  1978.     /**
  1979.      * boolean property representing whether the text will be scrolled in from the
  1980.      * edge of the panel on a mouse over, or popped up beside the component.
  1981.      * @see #setTextScrollsIn
  1982.      * @see #getTextScrollsIn
  1983.      */
  1984.     protected boolean textScrollsIn = true;
  1985.  
  1986.     /**
  1987.      * int property which represents how long (in milliseconds) to pause before
  1988.      * starting to scroll/pop-up text on a mouse entered event.
  1989.      * @see #setTextDelay
  1990.      * @see #getTextDelay
  1991.      */
  1992.     protected int textDelay = 0;
  1993.  
  1994.     /**
  1995.      * boolean property representing whether the text will be drawn transparently
  1996.      * rather than using the specified textHighlightColor.
  1997.      * @see #setHighlightText
  1998.      * @see #getHighlightText
  1999.      */
  2000.     protected boolean highlightText = false;
  2001.  
  2002.     /**
  2003.      * The color used for text when this component is enabled.
  2004.      * @see #getTextColor
  2005.      * @see #setTextColor
  2006.      */
  2007.     protected Color textColor = null;
  2008.  
  2009.     /**
  2010.      * Color which will be used to highlight the text if highlightText flag is set.
  2011.      * @see #setTextHighlightColor
  2012.      * @see #getTextHighlightColor
  2013.      */
  2014.     protected Color textHighlightColor = null;
  2015.  
  2016.     /**
  2017.      * Timer object which is used to time the delay before popping up the text.
  2018.      */
  2019.     protected Timer delayTimer = null;
  2020.  
  2021.     /**
  2022.      * Reference to the Mouse object which is listening to myComponent for mouse events.
  2023.      */
  2024.     protected Mouse myMouseListener = null;
  2025.  
  2026.     /**
  2027.      * Extra width which is added on if highlighText == true
  2028.      */
  2029.     protected int extraHighlightWidth;
  2030.  
  2031.     /**
  2032.      * Extra height which is added on if highlighText == true
  2033.      */
  2034.     protected int extraHighlightHeight;
  2035.  
  2036.     /**
  2037.      * Defines the "scrolling in" scroll state.  One of four possible values for scrollState.
  2038.      */
  2039.     protected static final int SCROLLING_IN = 0;
  2040.     /**
  2041.      * Defines the "done scrolling in" scroll state.  One of four possible values for scrollState.
  2042.      */
  2043.     protected static final int DONE_SCROLLING_IN = 1;
  2044.     /**
  2045.      * Defines the "scrolling out" scroll state.  One of four possible values for scrollState.
  2046.      */
  2047.     protected static final int SCROLLING_OUT = 2;
  2048.     /**
  2049.      * Defines the "done scrolling out" scroll state.  One of four possible values for scrollState.
  2050.      */
  2051.     protected static final int DONE_SCROLLING_OUT = 3;
  2052.     /**
  2053.      * Defines the "waiting to scroll in" scroll state.  One of four possible values for scrollState.
  2054.      */
  2055.     protected static final int WAITING_TO_SCROLL_IN = 4;
  2056.  
  2057.     /**
  2058.      * Error strings.
  2059.      */
  2060.     transient protected ResourceBundle errors;
  2061.  
  2062.     // Private members
  2063.     private LocationVeto locationVeto = null;
  2064.  
  2065.     private symantec.itools.beans.VetoableChangeSupport vetos = new symantec.itools.beans.VetoableChangeSupport(this);
  2066.     private symantec.itools.beans.PropertyChangeSupport changes = new symantec.itools.beans.PropertyChangeSupport(this);
  2067. }
  2068.